/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.matthewtamlin.fortytwo.library.util;

import ohos.agp.colors.RgbColor;
import ohos.agp.utils.Color;

/**
 * 工具类
 *
 * @since 2021-07-02
 */
public class Utils {
    private Utils() {
    }

    /**
     * 判空
     *
     * @param object object
     * @param message message
     * @param <T> T
     * @return T
     * @throws IllegalArgumentException exceptionMessage
     */
    public static <T> T checkNotNull(final T object, String message) {
        final String exceptionMessage = message == null ? "null check failed" : message;

        if (object != null) {
            return object;
        } else {
            throw new IllegalArgumentException(exceptionMessage);
        }
    }

    /**
     * Blends two colours to produce a single output colour. No check is done to ensure the provided
     * colors are valid ARGB hex codes, and providing invalid codes will result in an undefined
     * result. The blend works individually combining the ARGB components of the supplied colors and
     * then synthesising the components back into one color.
     *
     * @param color1 the first colour to blend, as an ARGB hex code
     * @param color2 the second colour to blend, as an ARGB hex code
     * @param ratio the proportion of {@code color2} to use in the blended result, between 0 and 1 (inclusive)
     * @return the ARGB code for the blended colour
     * @throws IllegalArgumentException if {@code ratio} is not between 0 and 1 (inclusive)
     */
    @SuppressWarnings("SameParameterValue")
    public static int blendColors(final int color1, final int color2, final float ratio) {
        if (ratio < 0 || ratio > 1) {
            throw new IllegalArgumentException("ratio must be between 0 and 1 (inclusive)");
        }

        // Calculate the inverse ratio once and cache the result to improve time performance
        final float inverseRatio = 1f - ratio;
        RgbColor rgbColor1 = RgbColor.fromArgbInt(color1);
        RgbColor rgbColor2 = RgbColor.fromArgbInt(color2);

        // Combine the colors using the ARGB components
        final float ac = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio);
        final float rc = (rgbColor1.getRed() * inverseRatio) + (rgbColor2.getRed() * ratio);
        final float gc = (rgbColor1.getGreen() * inverseRatio) + (rgbColor2.getGreen() * ratio);
        final float bc = (rgbColor1.getBlue() * inverseRatio) + (rgbColor2.getBlue() * ratio);

        // Compose the result from by combining the ARGB components
        return Color.argb((int) ac, (int) rc, (int) gc, (int) bc);
    }

    /**
     * Determines whether text should be black or white, depending on whichever maximises contrast
     * with the background color.
     *
     * @param backgroundColor the color of the background behind the text, as an ARGB hex code
     * @return white (0xFFFFFF) or black (0x000000)
     */
    public static int calculateBestTextColor(final int backgroundColor) {
        // sRGB [r, g, b]
        final int rgb = 255;
        RgbColor color = RgbColor.fromArgbInt(backgroundColor);
        final float[] preConversionValues = {((float) color.getRed()) / rgb,
            ((float) color.getGreen()) / rgb,
            ((float) color.getBlue()) / rgb};

        // linear RGB [r', g', b']
        final int size = 3;
        final float[] postConversionValues = new float[size];

        // sRGB to RGB according to https://goo.gl/vIj7TC
        final double tag = 0.04045;
        final double y1 = 12.92;
        final double y2 = 0.055;
        final double y3 = 1.055;
        final double y4 = 2.4;
        for (int i = 0; i < size; i++) {
            final float pos = preConversionValues[i];

            if (pos <= tag) {
                postConversionValues[i] = (float) (pos / y1);
            } else {
                postConversionValues[i] = (float) Math.pow((pos + y2) / y3, y4);
            }
        }

        // Luminance as derived from https://www.w3.org/TR/WCAG20/
        final int index = 2;
        final float luminance = (float) (0.2126 * postConversionValues[0]
            + 0.7152 * postConversionValues[1]
            + 0.0722 * postConversionValues[index]);

        final double lm = 0.179;
        if (luminance > lm) {
            return Color.BLACK.getValue();
        } else {
            return Color.WHITE.getValue();
        }
    }
}
